package com.openx.cloud.server.autoconfigure.feign;

import feign.Request;
import feign.RequestTemplate;
import feign.codec.EncodeException;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;

/**
 * @author yadu
 */
public class SpringEncoderExt implements Encoder {
    private static final Log log = LogFactory.getLog(SpringEncoderExt.class);
    private final SpringFormEncoder springFormEncoder = new SpringFormEncoder();
    private HttpMessageConverters messageConverters;

    public SpringEncoderExt(HttpMessageConverters messageConverters) {
        this.messageConverters = messageConverters;
    }

    @Override
    public void encode(Object requestBody, Type bodyType, RequestTemplate request) throws EncodeException {
        if (requestBody != null) {
            Class<?> requestType = requestBody.getClass();
            Collection<String> contentTypes = (Collection)request.headers().get("Content-Type");
            MediaType requestContentType = null;
            String message;
            if (contentTypes != null && !contentTypes.isEmpty()) {
                message = (String)contentTypes.iterator().next();
                requestContentType = MediaType.valueOf(message);
            }

            if (bodyType != null && bodyType.equals(MultipartFile.class)) {
                if (Objects.equals(requestContentType, MediaType.MULTIPART_FORM_DATA)) {
                    this.springFormEncoder.encode(requestBody, bodyType, request);
                } else {
                    message = "Content-Type \"" + MediaType.MULTIPART_FORM_DATA + "\" not set for request body of type " + requestBody.getClass().getSimpleName();
                    throw new EncodeException(message);
                }
            } else {
                Iterator var12 = this.messageConverters.getConverters().iterator();

                HttpMessageConverter messageConverter;
                do {
                    if (!var12.hasNext()) {
                        message = "Could not write request: no suitable HttpMessageConverter found for request type [" + requestType.getName() + "]";
                        if (requestContentType != null) {
                            message = message + " and content type [" + requestContentType + "]";
                        }

                        throw new EncodeException(message);
                    }

                    messageConverter = (HttpMessageConverter)var12.next();
                } while(!messageConverter.canWrite(requestType, requestContentType));

                if (log.isDebugEnabled()) {
                    if (requestContentType != null) {
                        log.debug("Writing [" + requestBody + "] as \"" + requestContentType + "\" using [" + messageConverter + "]");
                    } else {
                        log.debug("Writing [" + requestBody + "] using [" + messageConverter + "]");
                    }
                }

                FeignOutputMessage outputMessage = new FeignOutputMessage(request);

                try {
                    messageConverter.write(requestBody, requestContentType, outputMessage);
                } catch (IOException var11) {
                    throw new EncodeException("Error converting request body", var11);
                }

                request.headers((Map)null);
                request.headers(FeignUtil.getHeaders(outputMessage.getHeaders()));
                Charset charset;
                if (messageConverter instanceof ByteArrayHttpMessageConverter) {
                    charset = null;
                } else if (messageConverter instanceof ProtobufHttpMessageConverter && ProtobufHttpMessageConverter.PROTOBUF.isCompatibleWith(outputMessage.getHeaders().getContentType())) {
                    charset = null;
                } else {
                    charset = StandardCharsets.UTF_8;
                }

                request.body(Request.Body.encoded(outputMessage.getOutputStream().toByteArray(), charset));
            }
        }
    }

    private final class FeignOutputMessage implements HttpOutputMessage {
        private final ByteArrayOutputStream outputStream;
        private final HttpHeaders httpHeaders;

        private FeignOutputMessage(RequestTemplate request) {
            this.outputStream = new ByteArrayOutputStream();
            this.httpHeaders = FeignUtil.getHeaders(request.headers());
        }

        @Override
        public OutputStream getBody() throws IOException {
            return this.outputStream;
        }

        @Override
        public HttpHeaders getHeaders() {
            return this.httpHeaders;
        }

        public ByteArrayOutputStream getOutputStream() {
            return this.outputStream;
        }
    }



}
